package org.xiaodongwei.jvm.exe;

import tech.medivh.classpy.classfile.MethodInfo;
import tech.medivh.classpy.classfile.bytecode.Instruction;
import tech.medivh.classpy.classfile.constant.ConstantPool;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

// https://blog.csdn.net/gaosw0521/article/details/144362869
/**
 * JVM栈帧：栈帧可以看作是对当前执行函数的一次调用封装，这里的函数有程序员自定义的函数，有调用jdk提供的函数，还是一些是三方sdk中的函数
 *         用于支持方法调用和方法执行的关键数据结构
 * 1. 栈帧需要包含哪些内容？
 *    首先我们从一个方法入手，一个方法需要包含哪些内容呢？方法类型，方法返回值，方法名，方法参数列表，方法参数类型，方法参数对应的实际参数，方法体
 *    这些内容在栈帧中是如何对应的呢
 *
 *    public void say(String name) {
 *        System.out.println(name);
 *    }
 *
 *    栈帧的结构：
 *      局部变量表（Local Variables Table）
 *          1. 存储方法中的局部变量(方法体中定义的变量)和方法参数，当.java转化为字节码的时候，局部变量表的size就已经确定了
 *          2. 局部变量表的大小不仅取决于局部变量的数量，还与变量的类型和作用域有关。当一个局部变量的作用域结束时，它占用的局
 *             部变量表位置会被后续的局部变量取代
 *          3. 将局部变量的作用域最小化，可以增强代码的可读性和可维护性，并降低出错的可能性。
 *          4. 局部变量表的容量以槽（slot）为最小单位，一个槽可以容纳一个 32 位的数据类型（如 int）。对于占用 64 位的数据类型
 *            （如 double 和 long），会占用两个紧挨着的槽
 *      操作数栈（Operand Stack）
 *          1. 用于在方法执行过程中存储操作数和中间计算结果。与局部变量表类似，操作数栈的最大深度在编译时就已经确定，并写入到方法
 *             表的 Code 属性的 maximum stack size 中
 *          2. 操作数栈中的数据类型必须与字节码指令匹配。例如，iadd 指令只能用于整型数据的加法运算。在执行 iadd 指令时，栈顶的两
 *             个数据必须是 int 类型，不能出现一个 long 型和一个 double 型的数据进行 iadd 命令相加的情况。
 *      动态链接（Dynamic Linking）
 *          它允许在运行时根据对象的实际类型来解析方法调用。每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用，这个引用用于支持方法调用过程中的动态链接。
 *          1.
 *      方法返回地址（Return Address）
 *      附加信息（Additional Information）
 */
@SuppressWarnings("all")
public class JVMStackFrame {
    // 方法信息类
    private MethodInfo methodInfo;

    // 局部变量表，用于存储方法的参数以及方法体内的临时变量
    private Object[] localVariable;

    // 操作数栈，用于存储方法在执行过程中计算出来的中间结果，
    private Deque<Object> operateStack;

    // 常量池，用于存放编译期生成的各种字面量和符号引用 （属性名，类名，常量，参数引用名等等额外静态信息）
    private ConstantPool constantPool;

    // 指令集合，需要执行的jvm指令
    private List<Instruction> jvmCodes;

    // 当前执行指令在指令集合中的索引位置
    private int currentIndex;

    public ConstantPool getConstantPool() {
        return constantPool;
    }

    public Deque<Object> getOperateStack() {
        return operateStack;
    }

    public Object[] getLocalVariable() {
        return localVariable;
    }

    /**
     * 栈帧的构造
     * @param methodInfo：方法信息
     * @param constantPool：常量池
     * @param args：参数
     */
    public JVMStackFrame(MethodInfo methodInfo, ConstantPool constantPool, Object... args) {
        this.methodInfo = methodInfo;
        this.localVariable = new Object[methodInfo.getMaxLocals()];
        this.operateStack = new ArrayDeque<>();
        this.constantPool = constantPool;
        this.jvmCodes = methodInfo.getCodes();
        // 如果当前执行的函数有参数，则将函数参数拷贝到局部变量表中
        System.arraycopy(args, 0, localVariable, 0, args.length);
    }

    /**
     * 获取下一条jvm指令
     */
    public Instruction getNextInstruction() {
        if (jvmCodes == null || jvmCodes.isEmpty()) return null;
        return jvmCodes.get(currentIndex++);
    }

    /**
     * push到操作数栈
     */
    public void pushToOperateStack(Object object) {
        operateStack.push(object);
    }

    /**
     * 跳到对应的指令索引的位置，并更新currentIndex
     */
    public void jumpTo(int index) {
        for (int i = 0; i < jvmCodes.size(); i++) {
            Instruction instruction = jvmCodes.get(i);
            if (instruction.getPc() == index) {
                this.currentIndex = i;
                return;
            }
        }
    }
}
